#!/bin/bash

# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

. "$(dirname "$0")/common.sh" || exit 1
. "${SRC_ROOT}/platform/dev/toolchain_utils.sh" || exit 1

# Script must run inside the chroot
restart_in_chroot_if_needed "$@"

assert_not_root_user

# Developer-visible flags.
DEFINE_boolean configure $FLAGS_TRUE \
  "Update config files in <sysroot>/etc after installation."
DEFINE_boolean force $FLAGS_FALSE \
  "Install toolchain even if already up to date."
DEFINE_string toolchain "" \
  "Toolchain.  For example: i686-pc-linux-gnu, armv7a-softfloat-linux-gnueabi"
DEFINE_string sysroot "" "The sysroot to install the toolchain for."


FLAGS_HELP="usage: $(basename $0) [flags]

Installs cross toolchain libraries into a sysroot.  This script is not
meant to be used by developers directly.  Run at your own risk.
"

show_help_if_requested "$@"

# Parse command line flags
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# Only now can we die on error.  shflags functions leak non-zero error codes,
# so will die prematurely if 'switch_to_strict_mode' is specified before now.
switch_to_strict_mode

# Get the version number of a toolchain package.
cross_get_version() {
  local pkg=$1
  local toolchain=${2:-${FLAGS_toolchain}}
  local cpv
  if [[ "$CHOST" != "${toolchain}" ]]; then
    if [[  "$pkg" = "gcc" ]]; then
      # Users can install multiple versions of gcc at once, so we need to call
      # gcc-config to find out which installed version is selected.
      local path=$(CTARGET="${toolchain}" gcc-config -B || true)
      cpv=$(portageq owners / "$path" | sed -e '/^\t/d')
    else
      cpv=$(portageq match / "cross-${toolchain}/${pkg}" || true)
    fi
  else
    if [[ "$pkg" = glibc ]] ; then
      cpv=$(portageq match / sys-libs/glibc || true)
    elif [[ "$pkg" = gcc ]] ; then
      cpv=$(portageq match / sys-devel/gcc || true)
    else
      die "Unknown pkg ${pkg}"
    fi
  fi
  local cp=$(echo $cpv | sed -e 's/-r[0-9]*$//; s/-[^-]*$//')
  local result="${cpv#$cp-}"
  local count="$(echo $result | wc -w)"
  if [ "$count" -gt "1" ]; then
    die "Multiple versions of $pkg installed"
  elif [ "$count" -lt "1" ]; then
    die "Cannot find $pkg"
  fi
  echo $result
}

# Checks whether the libc version installed in the board
# matches the one installed by the toolchain.
board_needs_libc_update() {
  local board_version=$(get_variable "${SYSROOT_CACHE}" "LIBC_VERSION")
  local toolchain_version=$(cross_get_version glibc)
  if [[ "${board_version}" = "${toolchain_version}" ]]; then
    return 1
  fi
  return 0
}

install_toolchain_in_provided() {
  local gcc_ver="$1"
  local glibc_ver="$2"
  # Tell portage that toolchain packages are already installed in the sysroot.
  sudo mkdir -p "$BOARD_PROFILE"
  sudo_clobber "$BOARD_PROFILE/package.provided" << EOF
sys-devel/gcc-$gcc_ver
sys-libs/glibc-$glibc_ver
EOF
}

# Install all of the stuff that depends on the toolchain versions
# into the board root.
install_toolchain_in_board() {
  local cmds
  local gcc_ver=$(cross_get_version gcc)
  local libc_ver=$(cross_get_version glibc)
  if [[ -z ${gcc_ver} || -z ${libc_ver} ]]; then
    die "Cannot find toolchain to install into board root"
  fi
  echo "Installing the toolchain into the board root."
  # Untar glibc to get most of the headers required to build.

  # Install libc libraries.
  if [ "${CHOST}" != "$FLAGS_toolchain" ] ; then
    local libc_atom="cross-${FLAGS_toolchain}/glibc-${libc_ver}"
    local libc_path="${PKGDIR}/${libc_atom}.tbz2"

    cmds=()
    if [[ ! -e ${libc_path} ]]; then
        cmds+=( "emerge --nodeps -gf =${libc_atom}" )
    fi
    cmds+=(
      "tar jxpf '${libc_path}' -C '${BOARD_ROOT}' \
          './usr/${FLAGS_toolchain}' --strip-components=3"
      "mkdir -p '${BOARD_ROOT}/usr/lib/debug'"
      "tar jxpf '${libc_path}' -C '${BOARD_ROOT}/usr/lib/debug' \
          './usr/lib/debug/usr/${FLAGS_toolchain}' --strip-components=6 \
          || echo 'WARNING: libc debug info not copied.' >&2"
    )
    sudo_multi "${cmds[@]}"
  else
    sudo emerge --oneshot --nodeps -k --root='${BOARD_ROOT}' \
      =sys-libs/glibc-${libc_ver}
  fi

  if [[ ${FLAGS_configure} -eq ${FLAGS_TRUE} ]]; then
    install_toolchain_in_provided "$gcc_ver" "$libc_ver"

    set_variable "${SYSROOT_CACHE}" "LIBC_VERSION" "${libc_ver}"
  fi
}

set -u

if [[ -z "${FLAGS_sysroot}" ]]; then
  die "--sysroot required."
fi

BOARD_ROOT="${FLAGS_sysroot}"
BOARD_ETC="${BOARD_ROOT}/etc"
BOARD_PROFILE="${BOARD_ETC}/portage/profile"
SYSROOT_CACHE="${BOARD_ROOT}/${SYSROOT_SETTINGS_FILE}"

sysroot_toolchain="$(get_sysroot_config "${FLAGS_sysroot}" "CHOST")"
: ${FLAGS_toolchain:=${sysroot_toolchain}}

if [ -z "${FLAGS_toolchain}" ]; then
  die "No toolchain specified in the sysroot or on command line."
fi

eval "$(portageq envvar -v CHOST PKGDIR)"

if [[ ${FLAGS_force} -eq ${FLAGS_TRUE} ]] || \
    board_needs_libc_update; then
  info "Updating libc in the board."
  install_toolchain_in_board
else
  info "Cross toolchain already up to date.  Nothing to do."
fi
